const fs = require('fs');
const JSZip = require('jszip');
const _ = require('lodash');

class Zip {
  /**
   * 压缩目录
   * @param localPath
   * @param savePath
   * @param debug
   * @return {Promise<void>}
   */
  static async zip(localPath, savePath, debug = false) {
    const saveStat = await fs.lstat(savePath, (err, stats) => {
      return Promise.resolve(stats);
    });
    if (saveStat && saveStat.isDirectory())
      throw new Error('save path is folder');

    const zipConfigPath = localPath + '/.zipignore';
    let ignore;
    try {
      ignore = await fs.readFileSync(zipConfigPath, {encoding: 'utf8'});
    } catch (e) {
      ignore = undefined;
    }
    const files = await readDir(localPath, ignore);
    if (debug) {
      files.forEach(file => {
        console.log(file.file);
      });
      console.warn('debug mode, do not save');
      return;
    }

    const zip = new JSZip();
    files.forEach(file => {
      zip.file(file.file, file.data);
    });
    await new Promise(resolve => {
      zip.generateNodeStream({type: 'nodebuffer', streamFiles: true}).
        pipe(fs.createWriteStream(savePath)).
        on('finish', function() {
          resolve();
        });
    });
  }
}

module.exports = Zip;

/**
 * 循环读取目录所有文件
 * @param basePath
 * @param ignore
 * @return {Promise<[{file: String, data: Buffer}]>}
 */
async function readDir(basePath, ignore = '') {

  async function _readDir(relativePath) {
    const path = _.isEmpty(relativePath) ?
      basePath :
      basePath + '/' + relativePath;
    const _files = fs.readdirSync(path).filter(file => {
      return ignore.indexOf(file) === -1;
    });
    const files = [];
    for (let i = 0; i < _files.length; i++) {
      const file = _files[i];
      const relativeFilePath = relativePath + '/' + file;
      const filePath = path + '/' + file;
      const stat = await fs.lstatSync(filePath);
      if (stat.isFile()) {
        files.push({
          file: relativeFilePath.substring(1),
          data: fs.readFileSync(filePath),
        });
      }
      else if (stat.isDirectory()) {
        files.push(...await _readDir(relativeFilePath, ignore));
      }
    }
    return files;
  }

  return _readDir('', ignore);
}